package com.ft.security.utils;

import com.ft.core.exceptions.NotLoginException;
import com.ft.core.runtime.session.AuthUser;
import com.ft.core.runtime.session.IAuthUser;
import com.ft.security.SecurityConstants;
import com.ft.security.config.JwtProperties;
import com.ft.security.model.JwtToken;
import com.ft.security.model.JwtUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.*;
import java.util.function.Function;

/**
 * @copyright (C), 2015-2019, XXX有限公司
 * @fileName: JwtTokenUtil
 * @author: 李阳
 * @date: 2019/7/10 14:07
 * @description:
 * @history: <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
@Component
public class JwtTokenUtil implements Serializable {
    private static final long serialVersionUID = -8344142025980529302L;

    @Autowired
    private JwtProperties jwtProperties;

    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, p -> p.get("userName").toString());
    }

    public Long getTenantIdFromToken(String token) {
        return getClaimFromToken(token, p -> Long.valueOf(p.get("tenantId").toString()));
    }

    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }

    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    private Claims getAllClaimsFromToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(jwtProperties.getTokenSecret())
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception ex) {
            throw new NotLoginException("未登录");
        }

    }

    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    public JwtToken generateToken(JwtUser user) {
        return doGenerateToken(user);
    }

    private JwtToken doGenerateToken(JwtUser user) {

        Claims claims = Jwts.claims();
        claims.put("id", user.getId());
        claims.put("companyId", user.getCompanyId());
        claims.put("deptId", user.getDeptId());
        claims.put("tenantId", user.getTenantId());
        claims.put("userName", user.getUsername());
        claims.put("userType", user.getUserType());
        claims.put("rolePermissions", user.getAuthorities());

        final Date issuedAt = new Date(System.currentTimeMillis());
        final Date expiration = new Date(System.currentTimeMillis() + jwtProperties.getExpiration() * 1000);

        final String token = Jwts.builder()
                .setClaims(claims)
                .setIssuer("lee")
                .setIssuedAt(issuedAt)
                .setExpiration(expiration)
                .signWith(SignatureAlgorithm.HS256, jwtProperties.getTokenSecret())
                .compact();

        JwtToken jwtToken = new JwtToken();
        jwtToken.setAccessToken(token);
        jwtToken.setExpiresIn(expiration.getTime());
        jwtToken.setTokenType(jwtProperties.getTokenPrefix());

        return jwtToken;
    }

    public Boolean validateToken(String token) {
        return !isTokenExpired(token);
    }

    public Boolean validateToken(String token, JwtUser jwtUser) {
        final String username = getUsernameFromToken(token);
        return (
                username.equals(jwtUser.getUsername())
                        && !isTokenExpired(token));
    }

    public Collection<? extends GrantedAuthority> getAuthorities(String token) {
        final Claims claims = getAllClaimsFromToken(token);
        final List<LinkedHashMap> rolePermissions = claims.get("rolePermissions", List.class);

        Set<String> dbAuthSet = new HashSet<>();
        if (rolePermissions.size() > 0) {
            rolePermissions.forEach(rolePermission -> dbAuthSet.add(rolePermission.get("authority").toString()));
        }

        return AuthorityUtils.createAuthorityList(dbAuthSet.toArray(new String[0]));
    }


    public IAuthUser toAuthUser(String token) {
        final Claims claims = getAllClaimsFromToken(token);
        final List<LinkedHashMap> rolePermissions = claims.get("rolePermissions", List.class);

        AuthUser authUser = new AuthUser();
        authUser.setId(claims.get("id", Long.class));
        authUser.setTenantId(claims.get("tenantId", Long.class));
        authUser.setCompanyId(claims.get("companyId", Long.class));
        authUser.setDeptId(claims.get("deptId", Long.class));
        authUser.setUserType(claims.get("userType", Integer.class));
        authUser.setUserName(claims.get("userName", String.class));
        authUser.setRoles(rolePermissions.stream()
                .filter(p -> p.get("authority").toString().startsWith(SecurityConstants.ROLE))
                .map(p -> p.get("authority").toString().replace(SecurityConstants.ROLE, ""))
                .toArray(String[]::new));
        authUser.setPermissions(rolePermissions.stream()
                .filter(p -> !p.get("authority").toString().startsWith(SecurityConstants.ROLE))
                .map(p -> p.get("authority").toString())
                .toArray(String[]::new));

        return authUser;
    }
}
